import json
import socket
import struct
# from .params import IP, Port
from settings.settings import settings

from utils.decorator import singleton


@singleton
class AgvSocket(object):

    def __init__(self):
        self.cur_so = None
        self.cur_port = 0
        self.socket_pool = {}

    def init_socket_pool(self):
        if self.socket_pool:
            return True
        try:
            # 填充socket pool
            # self.__connect__(settings.AGV_IP, settings.AGV_PORT_STATUS)
            # self.__connect__(settings.AGV_IP, settings.AGV_PORT_OTHER)
            # self.__connect__(settings.AGV_IP, settings.AGV_PORT_PUSH)
            self.__connect__(settings.AGV_IP, settings.AGV_PORT_CONTROL)
            # self.__connect__(settings.AGV_IP, settings.AGV_PORT_NAVIGATE)
            return True
        except Exception as e:
            print(e)
            print('agv is closed!!!')
            return False

    def __connect__(self, ip, port):
        so = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            so.connect((ip, port))
            so.settimeout(5)
            # 同时将当前的socket存储起来
            self.socket_pool[port] = so
            print('server to agv connect success!!!')
            return so
        except Exception as e:
            print('server to agv connect fail!!!')
            # 关闭连接
            so.close()
            # 打印异常
            print(e)
            # 抛出异常
            raise Exception('server to agv disconnect')

    def send(self, so, req_id, msg_type, msg={}):
        raw_msg = self.__pack_msg__(req_id, msg_type, msg)
        try:
            so.send(raw_msg)
            return 'send success'
        except Exception as e:
            print('server to agv disconnect because long time or agv close!!!')
            print(e)
            # 字典中存在的socket,但是由于长时间未使用或者agv放断开,导致该socket是个异常socket,所以关闭当前socket,并且从socket池中清除
            self.cur_so.close()
            self.socket_pool.pop(self.cur_port)
            return 'send fail'

    def get(self, port):
        # 首先根据端口号去拿到已经存在的agv socket
        so = self.socket_pool.get(port)
        self.cur_so = so
        self.cur_port = port
        # 如果有直接返回,
        if so is not None:
            return so
        # 如果没有则重新创建,并返回
        try:
            self.__connect__(settings.AGV_IP, port)
            return self.socket_pool.get(port)
        except Exception as e:
            print(e)
            print('## server to agv disconnect')
            return None

    def receive(self, so):
        global data
        data_all = b''

        try:
            data = so.recv(16)
            so.settimeout(5)
        except socket.timeout:
            print('## agv socket timeout')
            so.close()
            raise Exception(
                'server to agv disconnect,beacause agv close or long time')

        json_data_len = 0
        back_req_num = 0

        if len(data) < 16:
            print('pack head error')
            so.close()
        else:
            header = struct.unpack('!BBHLH6s', data)
            json_data_len = header[3]
            back_req_num = header[4]
        data_all += data
        data = b''
        read_size = 1024
        try:
            while json_data_len > 0:
                recv = so.recv(read_size)
                data += recv
                json_data_len -= len(recv)
                if json_data_len < read_size:
                    read_size = json_data_len
            data_all += data
            return json.loads(data_all[16:].decode())

        except socket.timeout:
            print('receive data from agv fail!!!')
            so.close()
            return None

    def close(self, so):
        so.close()

    # 关闭所有的agv socket
    def close_socket_pool(self):
        for so in self.socket_pool.values():
            so.close()

    def __pack_msg__(self, req_id, msg_type, msg={}):
        msg_len = 0
        json_str = json.dumps(msg)

        if msg != {}:
            msg_len = len(json_str)

        raw_msg = struct.pack('!BBHLH6s', 0x5A, 0x01, req_id,
                              msg_len, msg_type, b'\x00\x00\x00\x00\x00\x00')
        if msg != {}:
            raw_msg += bytearray(json_str, 'ascii')

        return raw_msg
